package departmentservice

import (
	"errors"
	"fmt"
	"strings"
	"time"
	"yunj/pkg/global"
	"yunj/pkg/yunj/core/builder"
	"yunj/pkg/yunj/core/builder/builderconsts"
	"yunj/pkg/yunj/core/builder/builderdto"
	"yunj/pkg/yunj/core/builder/builderenum"
	"yunj/pkg/yunj/core/builder/form"
	"yunj/pkg/yunj/core/builder/form/button"
	"yunj/pkg/yunj/core/builder/form/field"
	"yunj/pkg/yunj/core/builder/table"
	"yunj/pkg/yunj/core/builder/table/col"
	"yunj/pkg/yunj/core/builder/table/col/action"
	"yunj/pkg/yunj/core/builder/table/deftoolbar"
	"yunj/pkg/yunj/core/builder/table/toolbar"
	"yunj/pkg/yunj/util"

	"yunj/pkg/yunj/app/dto"
	"yunj/pkg/yunj/app/enum/admin/departmentenum"
	"yunj/pkg/yunj/app/enum/stateenum"
	"yunj/pkg/yunj/app/model"
	"yunj/pkg/yunj/app/validator/admin/departmentpostionvalidator"

	"github.com/gin-gonic/gin"
	"github.com/worklz/go-validate"
	"gorm.io/gorm"
)

type postionPage struct {
}

var PostionPage *postionPage

// 列表构建器
func (s *postionPage) GetDepartment(ctx *gin.Context) (department *model.AdminDepartment, err error) {
	idStr := util.QueryParam(ctx, "id", "")
	if idStr == "" {
		err = errors.New("部门数据异常，请刷新页面后重试")
		return
	}
	id, err := util.ToUint64(idStr)
	if err != nil || id <= 0 {
		err = errors.New("部门异常，请刷新页面后重试")
		return
	}
	if dbErr := global.MySQL.Model(&model.AdminDepartment{}).Where("`id` = ? and state <> ?", id, stateenum.DELETED).First(&department).Error; dbErr != nil && !errors.Is(dbErr, gorm.ErrRecordNotFound) {
		err = fmt.Errorf("部门数据获取异常！%v", dbErr)
		return
	}
	if department.Id <= 0 {
		err = errors.New("部门不存在")
	}
	return
}

// 列表构建器
func (s *postionPage) ListBuilder(ctx *gin.Context) (tableBuilder *builder.Table, err error) {
	department, err := s.GetDepartment(ctx)
	if err != nil {
		return
	}
	tableBuilder = builder.NewTable(ctx, "PostionList").
		State([]builderdto.TableState{
			{Code: stateenum.NORMAL, Title: "正常"},
			{Code: stateenum.RECYLE_BIN, Title: "回收站"},
		}).
		Pk("department_postion_id").
		Page(false).
		Toolbar(func(t *builder.Table, state *builderdto.TableState) (actions []toolbar.ActionInterface, err error) {
			actions = []toolbar.ActionInterface{
				toolbar.NewOpenPopup("add", "添加").SetClass("layui-icon-add-circle").
					SetUrl(util.BuildAdminUrl("/department-postion/list/add", map[string]any{"id": department.Id})).SetAuth("yunj_department_postion_list_add"),
			}
			if state.Code == stateenum.RECYLE_BIN {
				actions = append(actions,
					toolbar.NewAsyncEvent(builderenum.TABLE_EVENT_TYPE_NORMAL, "恢复正常").SetDropdown(true).SetAuth("yunj_department_postion_list_normal"),
					toolbar.NewAsyncEvent(builderenum.TABLE_EVENT_TYPE_DELETED, "永久删除").SetDropdown(true).SetAuth("yunj_department_postion_list_deleted"),
				)
			} else {
				actions = append(actions,
					toolbar.NewAsyncEvent(builderenum.TABLE_EVENT_TYPE_RECYLE_BIN, "移入回收站").SetDropdown(true).SetAuth("yunj_department_postion_list_recyle_bin"),
				)
			}
			return
		}).
		DefaultToolbar([]deftoolbar.ActionInterface{
			deftoolbar.NewFilter(),
			deftoolbar.NewExport().SetAuth("yunj_department_postion_list_export"),
		}).
		Cols(func(t *builder.Table, state *builderdto.TableState) (cols []table.ColInterface, err error) {
			cols = []table.ColInterface{
				col.NewCheckbox("department_postion_id", "ID"),
				col.NewEnum("type", "类型").SetOptions(builder.GetEnumTableColOptions(departmentenum.PostionTypeEnum)).SetAlign(builderconsts.ALIGN_CENTER).SetMaxWidth(120),
				col.NewEnum("state", "状态").SetOptions(builder.GetEnumTableColOptions(stateenum.Enum)).SetAlign(builderconsts.ALIGN_CENTER).SetMaxWidth(80),
				col.NewText("postion_name", "岗位"),
				col.NewText("postion_code", "标识"),
				col.NewText("postion_intro", "简介"),
				col.NewEnum("postion_state", "岗位状态").SetOptions(builder.GetEnumTableColOptions(stateenum.Enum)).SetAlign(builderconsts.ALIGN_CENTER).SetMaxWidth(80),
			}
			actionCol := col.NewAction("操作").SetOptions([]action.ActionInterface{
				action.NewOpenPopup("edit", "详情").SetClass("layui-icon-survey").SetUrl(util.BuildAdminUrl("/department-postion/list/edit", map[string]any{"id": department.Id})).SetAuth("yunj_department_postion_list_edit"),
			})
			if state.Code == stateenum.RECYLE_BIN {
				actionCol.Options = append(actionCol.Options,
					action.NewAsyncEvent(builderenum.TABLE_EVENT_TYPE_NORMAL, "恢复正常").SetDropdown(true).SetAuth("yunj_department_postion_list_normal"),
					action.NewAsyncEvent(builderenum.TABLE_EVENT_TYPE_DELETED, "永久删除").SetDropdown(true).SetAuth("yunj_department_postion_list_deleted").SetConfirmFormFields(field.NewTxt("tips", "确认删除？")),
				)
			} else {
				actionCol.Options = append(actionCol.Options, action.NewAsyncEvent(builderenum.TABLE_EVENT_TYPE_RECYLE_BIN, "移入回收站").SetDropdown(true).SetAuth("yunj_department_postion_list_recyle_bin"))
			}
			cols = append(cols, actionCol)
			return
		}).
		Validator(func(t *builder.Table) (validate.ValidatorInterface, error) {
			v := &departmentpostionvalidator.DepartmentPostion{}
			v.InitValidator(v)
			return v, nil
		}).
		Count(func(t *builder.Table, requestParam *builderdto.TableRequestParam) (res builderdto.TableCountResult, err error) {
			// 状态数量查询
			query := s.listBuilderFilterQuery(department, requestParam)
			err = query.Count(&res.Count).Error
			if err != nil {
				err = fmt.Errorf("数据量查询异常！%v", err)
				return
			}
			return
		}).
		Items(func(t *builder.Table, requestParam *builderdto.TableRequestParam) (res builderdto.TableItemsResult, err error) {
			query := s.listBuilderFilterQuery(department, requestParam).Order("p.sort asc")
			var departmentPostionItems []*model.AdminDepartmentPostion
			if err = query.Find(&departmentPostionItems).Error; err != nil {
				err = fmt.Errorf("数据查询异常！%v", err)
				return
			}
			res.Items, err = s.listBuilderItemsHandle(departmentPostionItems)
			return
		}).
		Event(func(t *builder.Table, requestParam *builderdto.TableRequestParam) (res builderdto.TableEventResult, err error) {
			currTime := time.Now().In(time.Local)
			handleEventRes, err := builderenum.HandleEvent(ctx, &model.AdminDepartmentPostion{}, requestParam.EventCode, requestParam.Pks, func(param *builderenum.EventDbParam, e *builderenum.Event, pks []any) (err error) {
				if e.Value == builderenum.TABLE_EVENT_TYPE_DELETED {
					param.Data["deleted_at"] = &currTime
				}
				return
			})
			if err != nil {
				return
			}
			if handleEventRes != nil {
				res.ResponseJsonData = handleEventRes
				return
			}
			err = fmt.Errorf("异常事件[%v]", requestParam.EventCode)
			return
		})
	return
}

// 列表数据项处理
func (s *postionPage) listBuilderItemsHandle(items []*model.AdminDepartmentPostion) (resItems []*dto.AdminDepartmentPostionPageListItemResponse, err error) {
	if len(items) <= 0 {
		return
	}
	resItems = make([]*dto.AdminDepartmentPostionPageListItemResponse, 0, len(items))
	for _, item := range items {
		resItem := &dto.AdminDepartmentPostionPageListItemResponse{
			DepartmentPostionId: item.Id,
			Type:                item.Type,
			State:               item.State,
			PostionName:         item.PostionName,
			PostionCode:         item.PostionCode,
			PostionIntro:        item.PostionIntro,
			PostionState:        item.PostionState,
		}
		resItems = append(resItems, resItem)
	}
	return
}

// 列表过滤条件查询对象
func (s *postionPage) listBuilderFilterQuery(department *model.AdminDepartment, requestParam *builderdto.TableRequestParam) (query *gorm.DB) {
	tablePrefix := global.Config.Database.TablePrefix
	query = global.MySQL.Table(tablePrefix+"admin_department_postion as dp").
		Joins("JOIN "+tablePrefix+"admin_postion as p on dp.postion_id = p.id").
		Select("dp.*, p.name as postion_name, p.intro as postion_intro, p.code as postion_code, p.state as postion_state").
		Where("dp.department_id = ?", department.Id)

	// ids
	if len(requestParam.Pks) > 0 {
		query.Where("dp.id in ?", requestParam.Pks)
	}
	// state
	stateWhere := stateenum.NORMAL
	if stateCode, ok := stateenum.IsValue(requestParam.State); ok {
		stateWhere = stateCode
	}
	query.Where("dp.state = ?", stateWhere)
	return
}

// 表单构建器
func (s *postionPage) FormBuilder(ctx *gin.Context, isEdit bool) (formBuilder *builder.Form, err error) {
	department, err := s.GetDepartment(ctx)
	if err != nil {
		return
	}
	formBuilder = builder.NewForm(ctx, "PostionForm").
		Field(func(f *builder.Form, tab *builderdto.FormTab) (fields []form.FieldInterface, err error) {
			fields = []form.FieldInterface{
				field.NewDropdownSearch("postion_id", "岗位").SetVerify("required").SetOptionsApi(util.BuildAdminUrl("/department-postion/postionDropdownSearchOptions", map[string]any{"id": department.Id})),
				field.NewRadio("type", "类型").SetOptions(builder.GetEnumOptions(departmentenum.PostionTypeEnum)),
			}
			if isEdit {
				fields = append(fields, field.NewHidden("id"))
			}
			// 设置栅格布局
			for _, v := range fields {
				v.SetFieldGrid([]any{12, 6, "6 l3 r3"})
			}
			return
		}).
		Button(func(f *builder.Form) (buttons []form.ButtonInterface, err error) {
			if isEdit {
				buttons = []form.ButtonInterface{
					button.NewReset(),
					button.NewSubmit(),
				}
			} else {
				buttons = []form.ButtonInterface{
					button.NewClear(),
					button.NewSubmit(),
				}
			}
			return
		}).
		Validator(func(f *builder.Form) (validate.ValidatorInterface, error) {
			v := &departmentpostionvalidator.DepartmentPostion{}
			v.InitValidator(v)
			return v, nil
		}).
		Submit(func(f *builder.Form, values map[string]any) (res builderdto.FormSubmitResult, err error) {
			var dbDepartmentPostion *model.AdminDepartmentPostion
			dbDepartmentPostion = util.MapKeyValue(values, "dbDepartmentPostion", dbDepartmentPostion)
			dbDepartmentPostion.DepartmentId = department.Id
			if dbDepartmentPostion.Id > 0 {
				// 修改
				err = global.MySQL.Select("department_id", "postion_id", "type").Save(dbDepartmentPostion).Error
				if err != nil {
					err = fmt.Errorf("编辑失败！%v", err.Error())
					return
				}
			} else {
				// 新增
				err = global.MySQL.Select("department_id", "postion_id", "type", "created_at", "state").Create(dbDepartmentPostion).Error
				if err != nil {
					err = fmt.Errorf("新增失败！%v", err.Error())
					return
				}
			}
			res.Reload = true
			return
		})

	if isEdit {
		formBuilder.Load(func(f *builder.Form) (res any, err error) {
			departmentPostionId := util.QueryUint64Param(ctx, "department_postion_id")
			if departmentPostionId <= 0 {
				err = fmt.Errorf("异常数据")
				return
			}
			var data model.AdminDepartmentPostion
			dbErr := global.MySQL.Where("`id` = ? and state <> ?", departmentPostionId, stateenum.DELETED).First(&data).Error
			if (dbErr != nil && !errors.Is(dbErr, gorm.ErrRecordNotFound)) || data.PostionId <= 0 {
				err = fmt.Errorf("数据获取异常！%v", dbErr)
				return
			}
			res = util.StructToMap(data)

			return
		})
	}
	return
}

// 岗位下拉搜索选项
func (s *postionPage) PostionDropdownSearchOptions(ctx *gin.Context) (options []builderdto.Option, err error) {
	department, err := s.GetDepartment(ctx)
	if err != nil {
		return
	}
	param := util.QueryParams(ctx)

	// values
	tablePrefix := global.Config.Database.TablePrefix
	notExistsSubQuery := global.MySQL.Table(tablePrefix+"admin_department_postion dp").Select("1").
		Where("dp.postion_id = p.id and dp.department_id = ?", department.Id)
	query := global.MySQL.Table(tablePrefix+"admin_postion p").Where("p.state <> ?", stateenum.DELETED).Limit(20).Select("p.id,p.name,p.code")
	if values, exists := param["values"]; exists {
		if values, ok := values.(string); ok && values != "" {
			if ids := util.ToUint64Slice(strings.Split(values, ",")); len(ids) > 0 {
				query = query.Where("p.id in ?", ids)
				notExistsSubQuery.Where("dp.postion_id not in ?", ids)
			}
		}
	}
	query.Where("not exists (?)", notExistsSubQuery)

	// keywords
	if keywords, exists := param["keywords"]; exists {
		if keywords, ok := keywords.(string); ok && keywords != "" {
			likeKeywords := "%" + keywords + "%"
			query = query.Where("(p.name like ? or p.code like ?)", likeKeywords, likeKeywords)
		}
	}
	var items []model.AdminPostion
	dbErr := query.Find(&items).Error
	if dbErr != nil {
		err = fmt.Errorf("数据查询异常！%v", dbErr)
		return
	}
	options = make([]builderdto.Option, 0, len(items))
	for _, item := range items {
		txt := item.Name
		if item.Code != "" {
			txt += fmt.Sprintf("(%s)", item.Code)
		}
		options = append(options, builderdto.Option{Value: item.Id, Label: txt})
	}
	return
}
